﻿/* ListMenu Class
 * 
 * Discription:
 * This class lets you easly create simple menus that you can scroll 
 * trough with the arrow keys or the mouse
 * 
 * Usage:
 * To create a menu, create an object of the class ListMenu and call its constructor.
 * To add new menu items, call EditItem(itemnr, itemname);
 * Use the methods ProcessMouseClick and ProcessMouseMove every frame when needed (when the mouse is moved or clicked)
 * Every time you call the Up(), Down() or Select() methods, you need to call the Unlock() method before calling it another time.
 * This is to prevent accidental flooding of those methods (for example if you keep enter or up pressed for more than 1/60th second)
 * 
 * Note, you need to handle the control youself, that is, get the keyboard and mouse state every frame and call the controller functions 
 * (Up, Down, Select, ProcessMouseMove and ProcessMouseClick) everytime needed (when the up key is pressed for example)
 * 
 * To retrieve the selected menu item number, call the Select() method. This returns the current selected menu item as an int.
 * 
 * Other methods are used to change properties like color, position etc. See the function documentation for more information.
 * 
 * Author: Tigran Gasparian
 * 
 * If you want to use this class, please give me a mention in your credits.
 * Even if you modify it and make it look like you made this file.
 */

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;

public class ListMenu
{
    public int Scale;
    int DisplayScale;
    public float Aspect;
    SpriteFont spriteFont;
    SpriteBatch spriteBatch;
    Color TextColor = Color.White;
    Color SelectedColor = Color.DodgerBlue;
    Vector2 LineOffset;
    public Vector2 Position;
    Vector2 MaxElemSize = Vector2.Zero;
    public Vector2 MenuSize = Vector2.Zero;
    Vector2 MouseBorderPercentage = new Vector2(0.2f, 0.4f);
    string[] Elements;
    int SelectedItem;
    int numElems;

    public ListMenu(SpriteFont spriteFont, SpriteBatch spriteBatch, int numElems, Vector2 Position, int Scale, float Aspect)
    {
        this.spriteFont = spriteFont;
        this.spriteBatch = spriteBatch;
        this.numElems = numElems;
        Elements = new String[numElems];
        this.SelectedItem = 0;
        this.Position = Position;
        this.Scale = Scale;
        this.DisplayScale = Scale;
        this.Aspect = Aspect;
        this.LineOffset = new Vector2(0, 30f / Scale);
    }

    /* This function is used to make the box where the mouse is recorded wider/thiner.
     * a value of 1.0f makes it 100% wider on a side.
     * Default values are 0.2f and 0.4f which is 20% on left and 40% on right.
     */
    public void SetMouseBorderPercentage(float BorderLeft, float BorderRight)
    {
        this.MouseBorderPercentage = new Vector2(BorderLeft, BorderRight);
    }

    bool mouseIsInReach(Vector2 MousePos)
    {
        Vector2 tmpPos = new Vector2(Position.X * Aspect, Position.Y);
        Vector2 relMousePos = MousePos - tmpPos;
        if (relMousePos.X > -MouseBorderPercentage.X * MenuSize.X)
        {
            if (relMousePos.Y > 0)
            {
                if (relMousePos.X < MenuSize.X * (MouseBorderPercentage.Y + 1))
                {
                    if (relMousePos.Y < MenuSize.Y)
                    {
                        // Yep, the cursor is inside the menu
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /* Returns the currently selected item or -1 if no item was selected */
    public int ProcessMouseClick(Vector2 MousePos)
    {
        MousePos /= Scale;
        if (mouseIsInReach(MousePos))
        {
            return this.SelectedItem;
        }
        else
        {
            return -1;
        }
    }

    public void ProcessMouseMove(Vector2 MousePos)
    {
        MousePos /= Scale;
        if (mouseIsInReach(MousePos))
        {
            Vector2 relMousePos = MousePos - Position;
            this.SelectedItem = (int)Math.Floor(relMousePos.Y / (MenuSize.Y / this.numElems));
            if (this.SelectedItem >= this.numElems)
            {
                this.SelectedItem = this.numElems - 1;
            }
        }
    }

    /* Set the topleft position of the menu */
    public void SetPosition(Vector2 Position)
    {
        this.Position = Position;
    }

    /* Set the offset between each menu item */
    public void SetLineOffset(Vector2 Offset)
    {
        this.LineOffset = Offset;
    }

    public void EditItem(int elemNumber, string Text)
    {
        this.Elements[elemNumber] = Text;
        adjustMenuSize();
    }

    void adjustMenuSize()
    {
        calculateMaxElemSize();
        this.MenuSize = new Vector2(this.MaxElemSize.X + this.LineOffset.X * this.numElems, this.LineOffset.Y * this.numElems);
    }

    void calculateMaxElemSize()
    {
        // Get longest element and calculate the size of it.
        int longest = 0;
        int elem = 0;
        for (int i = 0; i < numElems; i++)
        {
            if (Elements[i] != null && Elements[i].Length > longest)
            {
                longest = Elements[i].Length;
                elem = i;
            }
        }
        this.MaxElemSize = this.spriteFont.MeasureString(this.Elements[elem])/Scale;
    }

    /* Set the color of the menu item currently selected */
    public void SetFocusColor(Color color)
    {
        this.SelectedColor = color;
    }

    /* Set the color of the menu items that are not selected */
    public void SetTextColor(Color color)
    {
        this.TextColor = color;
    }

    /* Moves the selection up by one */
    public void Up()
    {
        this.SelectedItem--;
        if (this.SelectedItem < 0)
        {
            this.SelectedItem = numElems - 1;
        }
    }

    /* Moves the selection down by one */
    public void Down()
    {
        this.SelectedItem++;
        this.SelectedItem = this.SelectedItem % numElems;
    }

    /* Returns the currently selected item */
    public int Select()
    {
        return SelectedItem;
    }

    /* Just a simple draw method */
    public void Draw()
    {
        for (int i = 0; i < numElems; i++)
        {
            if (i != this.SelectedItem)
            {
                Vector2 pos = (this.Position + LineOffset * i)*DisplayScale;
                pos.X *= Aspect;
                spriteBatch.DrawString(this.spriteFont, this.Elements[i], pos, this.TextColor);
            }
            else
            {
                Vector2 pos = (this.Position + LineOffset * i) * DisplayScale;
                pos.X *= Aspect;
                spriteBatch.DrawString(this.spriteFont, this.Elements[i], pos, this.SelectedColor);
            }
        }
    }
}